#pragma once

#include <cmath>

class Complex
{
protected:
    double real, image;

public:
    Complex(double first = 0, double second = 0, bool ifPolar = false)
        : real((ifPolar ? first * cos(second) : first)), image((ifPolar ? first * sin(second) : second)) {}
    Complex(const Complex &complex) : real(complex.real), image(complex.image) {}

// 加减法
#define COMPLEX_BINARY_OP(OpName, Op)                             \
    template <class T1, class T2>                                 \
    friend Complex OpName(const T1 &lhs, const T2 &rhs)           \
    {                                                             \
        Complex z1(lhs), z2(rhs);                                 \
        return Complex(z1.real Op z2.real, z1.image Op z2.image); \
    }

    COMPLEX_BINARY_OP(operator+, +)
    COMPLEX_BINARY_OP(operator-, -)
#undef COMPLEX_BINARY_OP

// 比较操作
#define COMPLEX_COMPARE_OP(OpName, Op)               \
    template <class T1, class T2>                    \
    friend bool OpName(const T1 &lhs, const T2 &rhs) \
    {                                                \
        Complex z1(lhs), z2(rhs);                    \
        return (z1.real Op z2.real ? true : false);  \
    }

    COMPLEX_COMPARE_OP(operator>, >)
    COMPLEX_COMPARE_OP(operator<, <)
    COMPLEX_COMPARE_OP(operator>=, >=)
    COMPLEX_COMPARE_OP(operator<=, <=)
#undef COMPLEX_COMPARE_OP

    template <class T1, class T2>
    friend bool operator==(const T1 &lhs, const T2 &rhs)
    {
        Complex z1(lhs), z2(rhs);
        return ((z1.real == z2.real) && (z1.image == z2.image) ? true : false);
    }

    template <class T1, class T2>
    friend Complex operator*(const T1 &lhs, const T2 &rhs)
    {
        Complex z1(lhs), z2(rhs);
        return Complex(z1.real * z2.real - z1.image * z2.image,
                       z1.real * z2.image + z1.image * z2.real);
    }

    template <class T1, class T2>
    friend Complex operator/(const T1 &lhs, const T2 &rhs)
    {
        Complex z1(lhs), z2(rhs);
        double module = z2.real * z2.real + z2.image * z2.image;
        return Complex((z1.real * z2.real + z1.image * z2.image) / module,
                       (z1.image * z2.real - z1.real * z2.image) / module);
    }

    template <class T>
    void operator=(const T &rhs)
    {
        Complex r(rhs);
        real = r.real;
        image = r.image;
    }

    double getReal() const { return real; }
    double getImage() const { return image; }

    bool ifReal() const { return (image == 0 ? true : false); }
    bool ifImage() const { return (real == 0 ? true : false); }

    friend std::ostream &operator<<(std::ostream &out, const Complex &complex)
    {
        out << complex.real;
        if (!complex.ifReal())
        {
            out << "," << complex.image << "i";
        }
        return out;
    }

    static inline double module(const Complex &z)
    {
        return sqrt(z.getReal() * z.getReal() + z.getImage() * z.getImage());
    }

    static inline Complex normalize(const Complex &z)
    {
        return Complex(z / module(z));
    }

    static inline double arg(const Complex &z)
    {
        return atan2(z.getImage(), z.getReal());
    }

    static inline Complex conjugate(const Complex &z)
    {
        return Complex(z.getReal(), -1 * z.getImage());
    }

    static inline Complex power(const Complex &z, double p)
    {
        return Complex(pow(module(z), p), arg(z) * p, true);
    }

    static inline auto sqrt_C(const double &t)
    {
        return (t < 0 ? Complex(0, sqrt(-1 * t)) : sqrt(t));
    }
    static inline Complex sqrt_C(const Complex &z)
    {
        return Complex(sqrt(module(z)), arg(z) / 2, true);
    }

    static inline Complex rotate(const Complex &z, double theta)
    {
        return Complex(module(z), arg(z) + theta, true);
    }
};